//
// Created by liuhuan on 2021/5/23.
//

#include <string>
#include "Coins.h"
#include "openssl/hmac.h"
#include "bitset"

using namespace std;

Coins::Coins(string key, uint data) {
    this->key = key;
    this->data = data;
    this->counter = 0;
    this->digest = hmac(key.c_str(), strlen(key.c_str()), this->data);
}

bool Coins::next() {
    if (counter == 127) {
        counter = 0;
    }
    return digest[counter++];
}

double Coins::draw() {
    uint out = 0;
    for (int i = 0; i < 32; i++) {
        out += next() ? pow(2, i) : 0;
    }
    return out / (pow(2, 32) - 1);
}

bitset<256> Coins::hmac(const char *key, size_t keyLen, uint data) {
    const char *msg = to_string(data).c_str();
    size_t msgLen = strlen(to_string(data).c_str());

    if (msg == NULL || key == NULL) {
        throw invalid_argument("Parameter error");
    }
    const EVP_MD *md = EVP_get_digestbyname("SHA256");
    unsigned char md_value[EVP_MAX_MD_SIZE] = "";
    unsigned int md_len = 0;

    HMAC_CTX *ctx;
    ctx = HMAC_CTX_new();
    HMAC_Init_ex(ctx, key, keyLen, md, NULL);
    HMAC_Update(ctx, reinterpret_cast<const unsigned char *>(msg), msgLen);
    HMAC_Final(ctx, md_value, &md_len);
    HMAC_CTX_free(ctx);

    bitset<32*8> bits;
    for (int i = 0; i < md_len; ++i) {
        unsigned char ch = md_value[i];
        for (int j = 7; j >= 0 && ch; --j) {
            if (ch & 0x1) {
                bits.set(8 * i + j);
            }
            ch >>= 1;
        }
    }
    return bits;
}
